#include <iostream>
#include <fstream>
#include "math/Math.hpp"
#include "math/Ray.hpp"

#include "usage/HittableList.hpp"
#include "usage/Sphere.hpp"
#include "usage/Camera.hpp"
#include "usage/material/Metal.hpp"
#include "usage/material/Lambertian.hpp"
#include "usage/material/Dielectric.hpp"

using namespace Stitches;

//double hit_sphere(const Vec3f& center, double radius, const Ray& r) {
//    Vec3f oc = r.origin() - center;
//    double a = dot(r.direction(), r.direction());
//    double b = 2.0 * dot(r.direction(), oc);
//    double c = dot(oc, oc) - radius * radius;
//    //一元二次方程的判别式 > 0 说明有两个解，也就是说，光线穿过球体
//    double discriminant = b * b - 4 * a * c;
//    if (discriminant < 0)
//    {
//        return -1.0f;
//    }
//    else
//    {
//        double t1 = (-b - sqrt(discriminant)) / (2.0 * a);
//        if (t1 > 0)
//            return t1;
//        double t2 = (-b + sqrt(discriminant)) / (2.0 * a);
//        if (t2 > 0)
//            return t2;
//        return -1.0;
//    }
//}
//
//double hit_sphere_simple(const Vec3f& center, double radius, const Ray& r) {
//    Vec3f oc = r.origin() - center;
//    double a = dot(r.direction(), r.direction());
//    double half_b = dot(r.direction(), oc);
//    double c = dot(oc, oc) - radius * radius;
//    //一元二次方程的判别式 > 0 说明有两个解，也就是说，光线穿过球体
//    double discriminant = half_b * half_b - a * c;
//    if (discriminant < 0)
//    {
//        return -1.0f;
//    }
//    else
//    {
//        double t1 = (-half_b - sqrt(discriminant)) / (a);
//        if (t1 > 0)
//            return t1;
//        double t2 = (-half_b + sqrt(discriminant)) / (a);
//        if (t2 > 0)
//            return t2;
//        return -1.0;
//    }
//
//}
//
//Vec3f color(const Ray& ray)
//{
//    Vec3f sphereCenter(0, 0, -1);
//    double t = hit_sphere_simple(sphereCenter, 0.5, ray);
//    if (t > 0)
//    {
//        //得到球面向量
//        Vec3f normal = glm::normalize(ray.at((float)t) - sphereCenter);
//        return 0.5f * Vec3f(normal.x + 1 , normal.y + 1, normal.z + 1);
//    }
//
//    Vec3f unit_direction = glm::normalize(ray.direction());
//    t = 0.5f * (unit_direction.y + 1.0f);
//    float temp = float(t);
//    return Vec3f(1.0, 1.0, 1.0) * (1.0f - temp) +
//            temp * Vec3f(0.5, 0.7, 1.0);
//}

/**----------------------------上面都是旧代码---------------------------------**/

const int max_depth = 50;

HittableList random_scene()
{
    HittableList world;

    auto ground_material = make_shared<Lambertian>(Vec3f(0.5, 0.5, 0.5));//地面
    world.add(make_shared<Sphere>(Vec3f(0, -1000, 0), 1000, ground_material));

    for (int a = -11; a < 11; a++) {
        for (int b = -11; b < 11; b++) {
            auto choose_mat = random_double(); //随机材质
            Vec3f center(a + 0.9 * random_double(), 0.2, b + 0.9 * random_double()); //随机中心

            if ((center - Vec3f(4, 0.2, 0)).length() > 0.9) {
                shared_ptr<Material> sphere_material;

                if (choose_mat < 0.8)
                {
                    // diffuse 漫反射
                    auto albedo = randomVec3() * randomVec3();
                    sphere_material = make_shared<Lambertian>(albedo);
                    world.add(make_shared<Sphere>(center, 0.2, sphere_material));
                }
                else if (choose_mat < 0.95) {
                    // metal 金属
                    auto albedo = randomVec3(0.5, 1);
                    auto fuzz = random_double(0, 0.5);
                    sphere_material = make_shared<Metal>(albedo, fuzz);
                    world.add(make_shared<Sphere>(center, 0.2, sphere_material));
                }
                else {
                    // glass 玻璃
                    sphere_material = make_shared<Dielectric>(1.5);
                    world.add(make_shared<Sphere>(center, 0.2, sphere_material));
                }
            }
        }
    }

    auto material1 = make_shared<Dielectric>(1.5);
    world.add(make_shared<Sphere>(Vec3f(0, 1, 0), 1.0, material1));

    auto material2 = make_shared<Lambertian>(Vec3f(0.4, 0.2, 0.1));
    world.add(make_shared<Sphere>(Vec3f(-4, 1, 0), 1.0, material2));

    auto material3 = make_shared<Metal>(Vec3f(0.7, 0.6, 0.5), 0.0);
    world.add(make_shared<Sphere>(Vec3f(4, 1, 0), 1.0, material3));

    return world;
}

Vec3f ray_color(const Ray& ray, const HittableList& world, int depth)
{
    if (depth <= 0)
        return Vec3f(0, 0, 0);

    HitRecord rec{};
    if (world.hit(ray, 0.001, infinity, rec))
    {
//        Vec3f target = rec.p + rec.normal + randomUnitVector();
//        Vec3f target = rec.p + randomInHemisphere(rec.normal);
//        return 0.5f * ray_color(Ray(rec.p, target - rec.p), world, depth - 1);

        Ray scattered;
        Vec3f attenuation;
        if (rec.mat_ptr->scatter(ray, rec, attenuation, scattered))
            return attenuation * ray_color(scattered, world, depth - 1);
        return Vec3f(0, 0, 0);
    }

    Vec3f unit_direction = glm::normalize(ray.direction());
    float t = 0.5f * (unit_direction.y + 1.0f);
    return Vec3f(1.0, 1.0, 1.0) * (1.0f - t) +
           t * Vec3f(0.5, 0.7, 1.0);
}

int main()
{

    std::ofstream outImage;
    outImage.open("Image.ppm");

    const double aspect_ratio = 16.0 / 9.0;
    int nx = 800;
    int ny = static_cast<int>(nx / aspect_ratio);
    const int samples_per_pixel = 100;

    HittableList world = random_scene();
//    auto material_ground = make_shared<Lambertian>(Vec3f(0.8, 0.8, 0.0));
////    auto material_center = make_shared<Lambertian>(Vec3f(0.7, 0.3, 0.3));
////    auto material_left = make_shared<Metal>(Vec3f(0.8, 0.8, 0.8), 0.3);
//    auto material_center = make_shared<Lambertian>(Vec3f(0.1, 0.2, 0.5));
//    auto material_left = make_shared<Dielectric>(1.5);
//    auto material_right = make_shared<Metal>(Vec3f(0.8, 0.6, 0.2), 0.0);
//
//
//    world.add(make_shared<Sphere>(Vec3f(0.0, -100.5, -1.0), 100.0, material_ground));
//    world.add(make_shared<Sphere>(Vec3f(0.0, 0.0, -1.0), 0.5, material_center));
//    world.add(make_shared<Sphere>(Vec3f(-1.0, 0.0, -1.0), 0.5, material_left));
//    world.add(make_shared<Sphere>(Vec3f(-1.0, 0.0, -1.0), -0.4, material_left));
//    world.add(make_shared<Sphere>(Vec3f(1.0, 0.0, -1.0), 0.5, material_right));

    Camera camera(Vec3f(-2, 2, 1), Vec3f(0, 0, -1), 20.0f, 16.0f / 9.0f);

    outImage << "P3\n" << nx << " " << ny << "\n255\n";
    for (int j = ny - 1; j >= 0; j--)
    {
        for (int i = 0; i < nx; i++)
        {

            Vec3f color(0, 0, 0);
            //做抗锯齿
            for (int k = 0; k < samples_per_pixel; ++k)
            {
                auto u = (i + random_double()) / (nx - 1);
                auto v = (j + random_double()) / (ny - 1);

                Ray r = camera.getRay(u, v);
                color += ray_color(r, world, max_depth);
            }

            float r = color.r;
            float g = color.g;
            float b = color.b;

            auto scale = 1.0 / samples_per_pixel;

            r = sqrt(r * scale);
            g = sqrt(g * scale);
            b = sqrt(b * scale);

            int ir = int(256 * clamp(r, 0.0, 0.999));
            int ig = int(256 * clamp(g, 0.0, 0.999));
            int ib = int(256 * clamp(b, 0.0, 0.999));
            outImage << ir << " " << ig << " " << ib << "\n";
        }
    }
    std::cout << "finish. \n" << std::endl;
    return 0;
}
